package cn.tutu.blockchain.config;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.Filter;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import cn.tutu.blockchain.sysuser.entity.SysPermission;
import cn.tutu.blockchain.sysuser.service.SysPermissionService;

@Configuration
public class ShiroConfig {

	@Autowired
	private SysPermissionService sysPermissionService;

	/**
	 * 项目自定义的Realm
	 * 
	 * @return
	 */
	@Bean
	public MyShiroRealm myShiroRealm() {
		MyShiroRealm myShiroRealm = new MyShiroRealm();
		myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
		return myShiroRealm;
	}

	/**
	 * 密码加密方式
	 * 
	 * @return
	 */
	@Bean
	public HashedCredentialsMatcher hashedCredentialsMatcher() {
		HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
		hashedCredentialsMatcher.setHashAlgorithmName("md5");// 散列算法:这里使用MD5算法;
		hashedCredentialsMatcher.setHashIterations(5);// 散列的次数，比如散列两次，相当于 md5(md5(""));
		return hashedCredentialsMatcher;
	}

	/**
	 * shiro自定义拦截器 需要在FilterRegistrationBean中设置setEnabled(false)，否则出现shiro过滤器加载顺序问题
	 * 
	 * @return
	 */
	@Bean
	public FilterRegistrationBean registration(KickoutSessionControlFilter filter) {
		FilterRegistrationBean registration = new FilterRegistrationBean(filter);
		registration.setEnabled(false);
		return registration;
	}

	/**
	 * 用户授权信息Cache, 采用EhCache
	 * 
	 * @return
	 */
	@Bean
	public EhCacheManager ehCacheManager() {
		EhCacheManager cacheManager = new EhCacheManager();
		cacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
		return cacheManager;
	}

	/**
	 * 会话ID生成器，用于生成会话ID，默认就是JavaUuidSessionIdGenerator，使用java.util.UUID生成
	 * 
	 * @return
	 */
	@Bean
	public JavaUuidSessionIdGenerator javaUuidSessionIdGenerator() {
		return new JavaUuidSessionIdGenerator();
	}

	@Bean
	public EnterpriseCacheSessionDAO sessionDao() {
		EnterpriseCacheSessionDAO sessionDao = new EnterpriseCacheSessionDAO();
		// 设置Session缓存名字，默认就是shiro-activeSessionCache，要和ehcache.xml中的那么对应
		sessionDao.setActiveSessionsCacheName("shiro-activeSessionCache");
		// 设置会话ID生成器
		sessionDao.setSessionIdGenerator(javaUuidSessionIdGenerator());

		return sessionDao;
	}

	/**
	 * 会话Cookie模板，sessionManager创建会话Cookie的模板
	 * 
	 * @return
	 */
	@Bean
	public SimpleCookie simpleCookie() {
		// 不修改使用默认的话，那么404的时候session就会过期
		SimpleCookie simpleCookie = new SimpleCookie("meta.session.id");
		// 如果设置为true，则客户端不会暴露给客户端脚本代码，使用HttpOnly cookie有助于减少某些类型的跨站点脚本攻击；
		// 此特性需要实现了Servlet 2.5 MR6及以上版本的规范的Servlet容器支持
		simpleCookie.setHttpOnly(true);
		// 设置Cookie的过期时间，秒为单位，默认-1表示关闭浏览器时过期Cookie
		simpleCookie.setMaxAge(-1);
		return simpleCookie;
	}

	@Bean
	public ExecutorServiceSessionValidationScheduler sessionValidationScheduler() {
		ExecutorServiceSessionValidationScheduler sessionScheduler = new ExecutorServiceSessionValidationScheduler();
		// 设置调度时间间隔，单位毫秒，默认就是1小时
		sessionScheduler.setInterval(1800000);
		// sessionScheduler.setSessionManager(sessionManager());
		return sessionScheduler;
	}

	/**
	 * 会话管理器
	 * 
	 * @return
	 */
	@Bean
	public DefaultWebSessionManager sessionManager() {

		DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();
		// 设置全局会话超时时间，默认30分钟，即如果30分钟内没有访问会话将过期 1800000
		defaultWebSessionManager.setGlobalSessionTimeout(1800000);
		// 删除失效的session
		defaultWebSessionManager.setDeleteInvalidSessions(true);
		// 是否开启会话验证器，默认是开启的
		// Shiro提供了会话验证调度器，用于定期的验证会话是否已过期，如果过期将停止会话；
		// 出于性能考虑，一般情况下都是获取会话时来验证会话是否过期并停止会话的；
		// 但是如在web环境中，如果用户不主动退出是不知道会话是否过期的，因此需要定期的检测会话是否过期，
		// Shiro提供了会话验证调度器SessionValidationScheduler来做这件事情。
		defaultWebSessionManager.setSessionValidationSchedulerEnabled(true);
		// Shiro提供SessionDAO用于会话的CRUD
		defaultWebSessionManager.setSessionDAO(sessionDao());
		// 是否启用/禁用Session Id Cookie，默认是启用的；
		// 如果禁用后将不会设置Session Id Cookie，即默认使用了Servlet容器的JSESSIONID，
		// 且通过URL重写（URL中的“;JSESSIONID=id”部分）保存Session Id。
		defaultWebSessionManager.setSessionIdCookieEnabled(true);
		defaultWebSessionManager.setSessionIdCookie(simpleCookie());

		defaultWebSessionManager.setCacheManager(ehCacheManager());
		return defaultWebSessionManager;
	}

	/**
	 * 限制同一账号登录同时登录人数控制
	 * 
	 * @return
	 */
	@Bean
	public KickoutSessionControlFilter kickoutSessionControlFilter() {
		KickoutSessionControlFilter kickoutSessionControlFilter = new KickoutSessionControlFilter();
		// 使用cacheManager获取相应的cache来缓存用户登录的会话；用于保存用户—会话之间的关系的；
		// 这里我们还是用之前shiro使用的redisManager()实现的cacheManager()缓存管理
		// 也可以重新另写一个，重新配置缓存时间之类的自定义缓存属性
		kickoutSessionControlFilter.setCacheManager(ehCacheManager());
		// 用于根据会话ID，获取会话进行踢出操作的；
		kickoutSessionControlFilter.setSessionManager(sessionManager());
		// 是否踢出后来登录的，默认是false；即后者登录的用户踢出前者登录的用户；踢出顺序。
		kickoutSessionControlFilter.setKickoutAfter(false);
		// 同一个用户最大的会话数，默认1；比如2的意思是同一个用户允许最多同时两个人登录；
		kickoutSessionControlFilter.setMaxSession(1);
		// 被踢出后重定向到的地址；
		kickoutSessionControlFilter.setKickoutUrl("/kickout");
		return kickoutSessionControlFilter;

	}

	@Bean
	public SecurityManager securityManager() {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(myShiroRealm());
		securityManager.setSessionManager(sessionManager());
		securityManager.setCacheManager(ehCacheManager());
		return securityManager;
	}

	@Bean
	public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {

		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		// 未登录提示
		shiroFilterFactoryBean.setLoginUrl("/unlogin");
		// 未授权提示;
		shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");

		// 自定义退出Filter
		Map<String, Filter> filterMap = shiroFilterFactoryBean.getFilters();
		filterMap.put("kickout", kickoutSessionControlFilter());
		shiroFilterFactoryBean.setFilters(filterMap);
		// 数据库读取权限配置拦截器.
		Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
		// 退出登录

		// 配置不会被拦截的链接 顺序判断
		/*
		 * filterChainDefinitionMap.put("/sysuser/ajaxLogin", "anon");
		 * filterChainDefinitionMap.put("/sysuser/ajaxRegs", "anon");
		 * filterChainDefinitionMap.put("/sysuser/resetPwd", "anon");
		 * filterChainDefinitionMap.put("/msg/sendMsg", "anon");
		 * filterChainDefinitionMap.put("/kickout", "anon");
		 * filterChainDefinitionMap.put("/logout", "logout");
		 */

		List<SysPermission> permissiones = sysPermissionService.findAll();
		for (SysPermission permission : permissiones) {
			filterChainDefinitionMap.put(permission.getUrl(), permission.getPermission());
		}
		filterChainDefinitionMap.put("/loginPage", "anon");
		filterChainDefinitionMap.put("/static/**", "anon");
		// filterChainDefinitionMap.put("/**", "kickout,authc");
		filterChainDefinitionMap.put("/**", "anon");
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
		return shiroFilterFactoryBean;
	}

	/**
	 * 开启shiro aop注解支持. 使用代理方式;所以需要开启代码支持;
	 * 
	 * @param securityManager
	 * @return
	 */
	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
		return authorizationAttributeSourceAdvisor;
	}

	public static void main(String[] args) {
		String hashAlgorithmName = "MD5";
		Object credentials = "123456";
		Object salt = ByteSource.Util.bytes("15013421554".getBytes());
		int hashIterations = 5;

		Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
		System.out.println(result);
	}

}
